package com.hhoss.snid;

import com.hhoss.boot.App;
import com.hhoss.lang.ClassMeta;
import com.hhoss.logs.Logger;
import com.hhoss.util.Random;

public class SNID {	
	private static final Logger logger = Logger.get();    
	
	private SeqNum sn = SeqNum.len5(); //[32768,65536)
	/** the function, scope, usage, sequence type, invoker */
	private int functionId;
	/** the location for site, cluster, group, city... */
	private int locationId;
	/** application runtime instance, relative with startup code */
	private int instanceId;
    
	private SNID(){};
    /**
     * if param not in the scope will using default value
     * @param function [1,32), will use caller hashcode if out of scope
     * @param location [1,64), will use machine hashcode if out of scope
     * @param instance [13,99), will use appCode if out of scope
     */
    public static SNID get(int function, int location, int instance){
    	SNID snid = new SNID();
    	snid.functionId= function>1 ? function : functionId();
    	snid.locationId= location>1 ? location : locationId();    	
    	snid.instanceId=(instance>12&&instance<100)?instance:appCode2();
    	return snid;
    }
    
    public static SNID get(){
    	SNID snid = new SNID();
    	snid.functionId= functionId();
    	snid.locationId= locationId();    	
    	snid.instanceId= appCode2();
    	return snid;
    }
    
    public String text(){
    	return Long.toString(next());
    }
    
    /**
     * @return sn long with info seconds[31],seqId[6],locId[6],insId[7],seqs[13]
     */
    public long next(){
    	long ins = SeqTims.now();
		 ins = functionId&0x1F|ins<<6; //6位，序列分类id, 5位seqId(0,32),+最高位符号0，
		 ins = locationId&0x3F|ins<<6; //6位，数据中心id， (0,64)
		 ins = instanceId&0x7F|ins<<7; //7位，应用实例id， (13,99)
 	    return sn.next()&0x1FFF|ins<<13; //[0,8192)
   }
    

    /**
     * @return int meta array for next seq fields int[8]{y,yyyy,M,d,h,m,s,insid,seq}
     */
    public int[] meta(){
    	int[] ts = SeqTims.meta();
    	int[] mt = new int[]{ts[0],ts[2],ts[3],ts[4],ts[5],ts[6],functionId&0x1F,locationId&0x3F,instanceId&0x7F,sn.next()&0x1FFF};
    	return mt;
    }
    
    /**
     * @param lsn long seq generated by Seq.next();
     * @return int array for seq fields
     */
    public static int[] meta(long lsn){
    	long ls = lsn;
    	int[] flds = new int[11];
    	flds[10]=(int)(ls&0x1FFF); 	ls>>=13;// cyclic Seq has 13 valid bit;
    	flds[9]=(int)(ls&0x7F); 	ls>>=7;// instance;
    	flds[8] =(int)(ls&0x3F); 	ls>>=6;// location;
    	flds[7] =(int)(ls&0x1F); //	ls>>=6;// function;
		 int[] ts = SeqTims.meta((int)(lsn>>32));
		 System.arraycopy(ts, 0, flds, 0, 7);
    	return flds;
    } 
    

	
    /**
     * 重新设置 application instance id. 
     * number of app instance id between [100,921]
     * 
     * 921 is substring(0,3) of 9216=[1024+8192}
     */
    protected static int fixCode3(){
    	int code = appCode()%10000;
    	if(code>999){code /= 10;}
		if(code>921){
			logger.warn("code[{}] out of [100,922).",code);    
			code = (code&0x1FF)+0x100;//[256,768)
  		}else if(code<100){
  			code+=100;
  		}
 		logger.info("set instance code[{}].",code);  
 		return code;
	} 
    
    /**
     * number of app instance id between [1000,9999]
     * 1. appCode%10000 if appCode>9999; <br />
     * 2. appCode+1000 if appCode<1000; <br />
     * @return int 
     */
    public static int appCode4(){
    	int code = appCode()%1000;    	
     	return code<100?(code+100):code;
    }
	
    /**
     * number of app instance id between [100,999]
     * 1. appCode%1000 if appCode>999; <br />
     * 2. appCode+100 if appCode<100; <br />
     * @return int 
     */
    public static int appCode3(){
    	int code = appCode()%1000;    	
     	return code<100?(code+100):code;
    }
    
    /**
     * number of app instance id between [13,99]  //means code<<13 in [106496,811008]
     * 1. appCode%100 if appCode>99; <br />
     * 2. appCode+13 if appCode<13; <br />
     * @return int 
     */
    public static int appCode2(){
    	int code = appCode()%100;
    	return code<13?(code+13):code;
    }    
    
    /**
     * return startup.code if set, or random[1024,9216) if not set startup.code <br />
     * if the code<13 then the code will using random[16,65552);
     * @return int number 
     */
    public static int appCode(){
    	int code = 0;
    	String asc = App.getStartupCode();//[1024,9216) if not set startupCode
    	if(asc!=null)try{
    		code = Integer.parseInt(asc);
    	}catch(Exception e){
    		logger.warn("Startup Code[{}] is not int.",asc);
    	}
       	if( code<13 ){// not set or too small!
    		code = new Random().nextInt(0xFFFF)+0x10;//[16,65552)
       	}
       	return code;
    }
    
    private static int functionId(){
    	String name = ClassMeta.invokerName();
    	return name.hashCode()&0x1F;
    }
    
    private static int locationId(){
    	String name = App.getClusterSite();
    	return name==null?0:name.hashCode()&0x3F;
    }
    
  
}
